////////////////////////////////////////////////////////
//
// 3D sample program
//
// Courtesy of Dr. Han-Wei Shen, Ohio State University
//
////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <math.h>
//#include <unistd.h>

#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glext.h>

#include <opencv/highgui.h>
#include <opencv/cv.h>

#include "ply.h"

GLhandleARB programObject;
GLhandleARB VertexShaderObject = 0;
GLhandleARB FragmentShaderObject = 0;

GLcharARB *vertexShaderSource;
GLcharARB *fragmentShaderSource;

GLuint texture;

int readShaderSource(char*, GLcharARB**, GLcharARB**);
bool LoadGLTextures();

/* trackball globals */
float angle = 0;
float lastPos[3] = { 0, 0, 0 };
float curPos[3];
int curx, cury;
int startx, starty;
float axis[3];
float trans[3];

float new_cx, new_cy, new_cz;

GLfloat eyeXform[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0,
		0, 0, 1 } };

GLdouble modelview[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, {
		0, 0, 0, 1 } };

GLdouble identity_modelview[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0,
		1, 0 }, { 0, 0, 0, 1 } };
GLdouble projection[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, {
		0, 0, 0, 1 } };

GLfloat globalMatrix[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 },
		{ 0, 0, 0, 1 } };

GLfloat modelMatrix[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, {
		0, 0, 0, 1 } };

GLfloat modelviewMatrix[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 },
		{ 0, 0, 1, 0 }, { 0, 0, 0, 1 } };

GLint viewport[4];

bool trackingMouse = 0;
bool trackballMove = 0;
bool redraw = 0;

float trackball_centerx, trackball_centery;
float trackball_width, trackball_height;

typedef struct Vertex {
	float x, y, z;
	float nx, ny, nz;
} Vertex;

typedef struct Face {
	unsigned int count;
	unsigned int *vertices;
	float nx, ny, nz;
} Face;

char* string_list[] = { "x", "y", "z", "nx", "ny", "nz", "vertex_indices" };

int win_width, win_height;

float cx, cy, cz;
float x_min, x_max, y_min, y_max, z_min, z_max;
float radius;

float eye_x = 0, eye_y = 0, eye_z = 0.5;

int press_x, press_y;
int release_x, release_y;
float x_angle = 0.0;
float y_angle = 0.0;
float scale_size = 1;
float local_scale = 1;

int xform_mode = 0;

#define XFORM_NONE    0 
#define XFORM_ROTATE  1
#define XFORM_SCALE 2 

int stage = 0;
int case_id = 0;
float base_translate = 0;
float low_rotate = 0;
float up_rotate = 90;
float hammer_rotate = 0;

int show_axis = -1;
int poly_fill = 0;

Vertex** vertices = 0;
Face** faces = 0;
unsigned int vertexcount;
unsigned int facecount;
int vertexnormals = 0;
int facenormals = 0;

bool transform_global = true;

bool use_glsl = true;

////////////////////////////////////////////////////////
// 4 x 4 matrix inversion - this is a hack because I return the 
// result to a global ...
//
float result[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0,
		0, 1 } };
void inverse(const float m[4][4]) {
	register int lp, i, j, k;
	static float wrk[4][8];
	static float a, b;

	for (i = 0; i < 4; i++) /* Set up matrices */
	{
		for (j = 0; j < 4; j++) {
			wrk[i][j] = m[i][j];
			wrk[i][j + 4] = 0.0;
			result[i][j] = 0.0;
		}
		wrk[i][i + 4] = 1.0;
	}

	for (lp = 0; lp < 4; lp++) /* Loop over all rows */
	{
		a = 0.0;
		j = (-1);
		for (i = lp; i < 4; i++) /* Find largest non-zero element */
		{
			b = wrk[i][lp];
			if (b < 0.0)
				b = (-b);
			if (b > a) {
				a = b;
				j = i;
			}
		}
		if (j != lp) /* If not on diagonal, put it there */
		{
			if (j < 0) /* Singular if none found */
				return;
			else
				/* Exchange rows from lp to end */
				for (k = lp; k < 8; k++) {
					a = wrk[j][k];
					wrk[j][k] = wrk[lp][k];
					wrk[lp][k] = a;
				}
		}
		a = wrk[lp][lp]; /* Normalize working row */
		for (i = lp; i < 8; i++)
			wrk[lp][i] /= a;

		for (i = lp + 1; i < 8; i++) /* Adjust rest of work space */
		{
			b = wrk[lp][i];
			for (j = 0; j < 4; j++) /* One column at a time */
				if (j != lp)
					wrk[j][i] -= wrk[j][lp] * b;
		}
	}

	for (i = 0; i < 4; i++) /* Return result matrix */
		for (j = 0; j < 4; j++)
			result[i][j] = (float) wrk[i][j + 4];
	return;
}

///////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//
void store_ply(PlyFile* input, Vertex ***vertices, Face ***faces,
		unsigned int* vertexcount, unsigned int* facecount, int* vertexnormals,
		int* facenormals) {
	int i, j;

	// go through the element types
	for (i = 0; i < input->num_elem_types; i = i + 1) {
		int count;

		// setup the element for reading and get the element count
		char* element = setup_element_read_ply(input, i, &count);

		// get vertices
		if (strcmp("vertex", element) == 0) {
			*vertices = (Vertex**) malloc(sizeof(Vertex) * count);
			*vertexcount = count;

			// run through the properties and store them
			for (j = 0; j < input->elems[i]->nprops; j = j + 1) {
				PlyProperty* property = input->elems[i]->props[j];
				PlyProperty setup;

				if (strcmp("x", property->name) == 0 && property->is_list
						== PLY_SCALAR) {

					setup.name = string_list[0];
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, x);
					setup.count_internal = 0;
					setup.count_offset = 0;

					setup_property_ply(input, &setup);
				} else if (strcmp("y", property->name) == 0
						&& property->is_list == PLY_SCALAR) {

					setup.name = string_list[1];
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, y);
					setup.count_internal = 0;
					setup.count_offset = 0;

					setup_property_ply(input, &setup);
				} else if (strcmp("z", property->name) == 0
						&& property->is_list == PLY_SCALAR) {

					setup.name = string_list[2];
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, z);
					setup.count_internal = 0;
					setup.count_offset = 0;

					setup_property_ply(input, &setup);
				} else if (strcmp("nx", property->name) == 0
						&& property->is_list == PLY_SCALAR) {

					setup.name = string_list[3];
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, nx);
					setup.count_internal = 0;
					setup.count_offset = 0;

					setup_property_ply(input, &setup);
					*vertexnormals = 1;
				} else if (strcmp("ny", property->name) == 0
						&& property->is_list == PLY_SCALAR) {

					setup.name = string_list[4];
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, ny);
					setup.count_internal = 0;
					setup.count_offset = 0;

					setup_property_ply(input, &setup);
					*vertexnormals = 1;
				} else if (strcmp("nz", property->name) == 0
						&& property->is_list == PLY_SCALAR) {

					setup.name = string_list[5];
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, nz);
					setup.count_internal = 0;
					setup.count_offset = 0;

					setup_property_ply(input, &setup);
					*vertexnormals = 1;
				}
				// dunno what it is
				else {
					fprintf(stderr, "unknown property type found in %s: %s\n",
							element, property->name);
				}
			}

			// do this if you want to grab the other data
			// list_pointer = get_other_properties_ply
			//                (input, offsetof(Vertex, struct_pointer));

			// copy the data
			for (j = 0; j < count; j = j + 1) {
				(*vertices)[j] = (Vertex*) malloc(sizeof(Vertex));

				get_element_ply(input, (void*) ((*vertices)[j]));
			}
		}
		// get faces
		else if (strcmp("face", element) == 0) {
			*faces = (Face**) malloc(sizeof(Face) * count);
			*facecount = count;

			// run through the properties and store them
			for (j = 0; j < input->elems[i]->nprops; j = j + 1) {
				PlyProperty* property = input->elems[i]->props[j];
				PlyProperty setup;

				if (strcmp("vertex_indices", property->name) == 0
						&& property->is_list == PLY_LIST) {

					setup.name = string_list[6];
					setup.internal_type = Uint32;
					setup.offset = offsetof(Face, vertices);
					setup.count_internal = Uint32;
					setup.count_offset = offsetof(Face, count);

					setup_property_ply(input, &setup);
				} else if (strcmp("nx", property->name) == 0
						&& property->is_list == PLY_SCALAR) {

					setup.name = string_list[3];
					setup.internal_type = Float32;
					setup.offset = offsetof(Face, nx);
					setup.count_internal = 0;
					setup.count_offset = 0;

					setup_property_ply(input, &setup);
					*facenormals = 1;
				} else if (strcmp("ny", property->name) == 0
						&& property->is_list == PLY_SCALAR) {

					setup.name = string_list[4];
					setup.internal_type = Float32;
					setup.offset = offsetof(Face, ny);
					setup.count_internal = 0;
					setup.count_offset = 0;

					setup_property_ply(input, &setup);
					*facenormals = 1;
				} else if (strcmp("nz", property->name) == 0
						&& property->is_list == PLY_SCALAR) {

					setup.name = string_list[5];
					setup.internal_type = Float32;
					setup.offset = offsetof(Face, nz);
					setup.count_internal = 0;
					setup.count_offset = 0;

					setup_property_ply(input, &setup);
					*facenormals = 1;
				}
				// dunno what it is
				else {
					fprintf(stderr, "unknown property type found in %s: %s\n",
							element, property->name);
				}
			}

			// do this if you want to grab the other data
			// list_pointer = get_other_properties_ply
			//                (input, offsetof(Face, struct_pointer));

			// copy the data
			for (j = 0; j < count; j = j + 1) {
				(*faces)[j] = (Face*) malloc(sizeof(Face));

				get_element_ply(input, (void*) ((*faces)[j]));
			}
		}
		// who knows?
		else {
			fprintf(stderr, "unknown element type found: %s\n", element);
		}
	}

	// if you want to grab the other data do this
	// get_other_element_ply(input);
}

void trackball_project(int x, int y, int centerx, int centery, int w, int h,
		float v[3]) {
	float d, a;

	v[0] = (x - centerx) / (0.5 * w);
	if (v[0] < -1)
		v[0] = -1;
	if (v[0] > 1)
		v[0] = 1;
	v[1] = (y - centery) / (0.5 * h);
	if (v[1] < -1)
		v[1] = -1;
	if (v[1] > 1)
		v[1] = 1;
	v[2] = 0.0f;

	d = sqrt(v[0] * v[0] + v[1] * v[1]);
	d = (d < 1.0) ? d : 1.0f;

	//v[2] = cos ((PI/2.0)*((d < 1.0)?d:1.0));
	v[2] = sqrt(1.001 - d * d);

	a = 1.0 / (sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]));

	v[0] *= a;
	v[1] *= a;
	v[2] *= a;
}

///////////////////////////////////////////

void draw_axes() {
	glLineWidth(3);
	glBegin(GL_LINES);
	glColor3f(1, 0, 0); // red: x; green: y; blue: z
	glVertex3f(0, 0, 0);
	glVertex3f(4, 0, 0);
	glColor3f(0, 1, 0);
	glVertex3f(0, 0, 0);
	glVertex3f(0, 4, 0);
	glColor3f(0, 0, 1);
	glVertex3f(0, 0, 0);
	glVertex3f(0, 0, 4);
	glEnd();
	glLineWidth(1.0);
}

///////////////////////////////////////
void draw_model() {
	glPushMatrix();
	glMultMatrixf((const float*) modelMatrix);
	glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*) modelviewMatrix);
	if (show_axis == 1) {
		glPushMatrix();
		glScalef(.1, .1, .1);
		draw_axes();
		glPopMatrix();
	}
	glColor3f(1, 1, 1);
	for (int i = 0; i < facecount; i++) {
		glBegin(GL_POLYGON);
		for (int j = 0; j < faces[i]->count; j++) {
			float x = vertices[faces[i]->vertices[j]]->x / 1000;
			float y = vertices[faces[i]->vertices[j]]->y / 1000;
			float z = vertices[faces[i]->vertices[j]]->z / 1000;
			if (vertexnormals) {
				float nx = vertices[faces[i]->vertices[j]]->nx;
				float ny = vertices[faces[i]->vertices[j]]->ny;
				float nz = vertices[faces[i]->vertices[j]]->nz;
				glNormal3f(nx, ny, nz);
			}
			glVertex3f(x, y, z);
		}
		glEnd();
	}
	//draw the trackball for the model
	if (transform_global == false) {
		glPushMatrix();
		glTranslatef(cx, cy, cz); //translate the sphere to enclose the object
		glGetDoublev(GL_MODELVIEW_MATRIX, (GLdouble*) modelview);
		glColor3f(0, 0, 1);
		glutWireSphere(radius, 30, 30);
		glPopMatrix();
	}
	glPopMatrix();
}

void set_lighting() {
	GLfloat light_ambient[] = { .0, .0, .0, 1 };
	GLfloat light_diffuse[] = { .8, .8, .8, 1 };
	GLfloat light_specular[] = { 1, 1, 1, 1 };

	GLfloat mat_specular[] = { .7, .7, .7, 1 };
	GLfloat mat_shine[] = { 60 };

	//  glShadeModel(GL_FLAT);
	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
	glEnable(GL_NORMALIZE);
	glEnable(GL_COLOR_MATERIAL);
	glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shine);

}

//////////////////////////////////////////////////////

void display() {
	GLfloat light_pos[] = { 0, 0, 0, 1 };

	glClearColor(0, 0, 0, 1);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glEnable(GL_DEPTH_TEST);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);

	set_lighting();

	//----------------------------------------------
	// now define projection and modelview matrix
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60, 1, .001, 10000);
	glGetDoublev(GL_PROJECTION_MATRIX, (GLdouble*) projection);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
	gluLookAt(eye_x, eye_y, eye_z, 0, 0, 0, 0, 1, 0);

	//apply the global transformation to the whole world
	glMultMatrixf((const float*) globalMatrix);

	// change the polygon drawing mode as well as the lighting mode
	if (show_axis == 1) {
		glDisable(GL_LIGHTING);
		draw_axes();
		glEnable(GL_LIGHTING);
	}
	if (!poly_fill)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	else
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	if (!poly_fill)
		glDisable(GL_LIGHTING);
	else
		glEnable(GL_LIGHTING);

	//----------------------------------------------
	// now draw some simiple objects in the scene
	/*  glColor3f(0,1,0);
	 glPushMatrix();
	 glTranslatef(0,0.03,0);
	 glScalef(1, .01, 1);       // floor
	 glutSolidCube(1);
	 glPopMatrix();

	 glColor3f(1,0,0);
	 glPushMatrix();
	 glTranslatef(.3, 0.08, .3);     // cube object
	 glutSolidCube(.1);
	 glPopMatrix();

	 glColor3f(0,0,1);
	 glPushMatrix();
	 glTranslatef(-.3, 0.12, -.3);   // sphere object
	 glutSolidSphere(.1, 10, 10);
	 glPopMatrix(); */

	//----------------------------------------------
	// now draw my ply model(s)
	GLint loc;
	//loc = glGetUniformLocationARB(programObject, "envMap");
	//glUniform1iARB(loc, texture);

	//glActiveTextureARB(GL_TEXTURE0_ARB);
	glBindTexture(GL_TEXTURE_2D, texture);
	glEnable(GL_TEXTURE_2D);

	draw_model();

	//----------------------------------------------
	// now swap buffer to draw
	glutSwapBuffers();
	glGetIntegerv(GL_VIEWPORT, (GLint*) viewport);
}

///////////////////////////////////////////////////////////////////
//          Mouse callbacks to specify transformations 
///////////////////////////////////////////////////////////////////
void mymouse(int button, int state, int x, int y) {
	GLdouble winx, winy, winz;
	GLdouble winx2, winy2, winz2;

	if (transform_global == true) {
		if (state == GLUT_DOWN) {
			press_x = x;
			press_y = y;
			if (button == GLUT_LEFT_BUTTON)
				xform_mode = XFORM_ROTATE;
			else if (button == GLUT_RIGHT_BUTTON)
				xform_mode = XFORM_SCALE;
		} else if (state == GLUT_UP) {
			xform_mode = XFORM_NONE;
		}
	} else {
		if (state == GLUT_DOWN) {
			press_x = x;
			press_y = y;
			if (button == GLUT_LEFT_BUTTON) {
				xform_mode = XFORM_ROTATE;
				gluProject(0, 0, 0, (const GLdouble*) modelview,
						(const GLdouble*) projection, (const GLint*) viewport,
						&winx, &winy, &winz);
				float radiusv[3];
				radiusv[0] = modelview[0][0] * radius;
				radiusv[1] = modelview[0][1] * radius;
				radiusv[2] = modelview[0][2] * radius;
				float new_radius = sqrt(radiusv[0] * radiusv[0] + radiusv[1]
						* radiusv[1] + radiusv[2] * radiusv[2]);
				gluProject(cx + new_radius - eye_x, cy - eye_y, cz - eye_z,
						(const GLdouble*) identity_modelview,
						(const GLdouble*) projection, (const GLint*) viewport,
						&winx2, &winy2, &winz2);

				printf("sphere center is at %lf %lf %lf\n", winx, winy, winz);
				printf("sphere point is at %lf %lf %lf\n", winx2, winy2, winz2);
				float project_radius = sqrt((winx - winx2) * (winx - winx2)
						+ (winy - winy2) * (winy - winy2) + (winz - winz2)
						* (winz - winz2));
				printf("projected sphere radius = %f\n", project_radius);

				trackball_centerx = winx;
				trackball_centery = winy;
				trackball_width = 4 * project_radius;
				trackball_height = 4 * project_radius;
				trackball_project(press_x, win_height - press_y,
						(int) trackball_centerx, (int) trackball_centery,
						(int) trackball_width, (int) trackball_height, curPos);
				lastPos[0] = curPos[0];
				lastPos[1] = curPos[1];
				lastPos[2] = curPos[2];
			} else if (button == GLUT_RIGHT_BUTTON)
				xform_mode = XFORM_SCALE;
		} else if (state == GLUT_UP) {
			xform_mode = XFORM_NONE;
		}
	}
}

/////////////////////////////////////////////////////////

void mymotion(int x, int y) {
	float dx, dy, dz;

	if (transform_global == true) {
		if (xform_mode == XFORM_ROTATE) {
			x_angle += (x - press_x) / 5.0;
			if (x_angle > 180)
				x_angle -= 360;
			else if (x_angle < -180)
				x_angle += 360;
			press_x = x;

			y_angle += (y - press_y) / 5.0;
			if (y_angle > 180)
				y_angle -= 360;
			else if (y_angle < -180)
				y_angle += 360;
			press_y = y;
		} else if (xform_mode == XFORM_SCALE) {
			float old_size = scale_size;
			scale_size *= (1 + (y - press_y) / 60.0);
			if (scale_size < 0)
				scale_size = old_size;
			press_y = y;
		}
		glMatrixMode(GL_MODELVIEW);
		//  global transformation
		glLoadIdentity();
		glRotatef(x_angle, 0, 1, 0);
		glRotatef(y_angle, 1, 0, 0);
		glScalef(scale_size, scale_size, scale_size);
		glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*) globalMatrix);
	} else { // local transformation
		if (xform_mode == XFORM_ROTATE) {
			press_x = x;
			press_y = y;
			trackball_project(press_x, win_height - press_y,
					(int) trackball_centerx, (int) trackball_centery,
					(int) trackball_width, (int) trackball_height, curPos);
			dx = curPos[0] - lastPos[0];
			dy = curPos[1] - lastPos[1];
			dz = curPos[2] - lastPos[2];
			angle = 90 * sqrt(dx * dx + dy * dy + dz * dz);
			axis[0] = lastPos[1] * curPos[2] - lastPos[2] * curPos[1];
			axis[1] = lastPos[2] * curPos[0] - lastPos[0] * curPos[2];
			axis[2] = lastPos[0] * curPos[1] - lastPos[1] * curPos[0];
			lastPos[0] = curPos[0];
			lastPos[1] = curPos[1];
			lastPos[2] = curPos[2];

			inverse(modelviewMatrix);
			float local_axis[3];
			local_axis[0] = result[0][0] * axis[0] + result[1][0] * axis[1]
					+ result[2][0] * axis[2];
			local_axis[1] = result[0][1] * axis[0] + result[1][1] * axis[1]
					+ result[2][1] * axis[2];
			local_axis[2] = result[0][2] * axis[0] + result[1][2] * axis[1]
					+ result[2][2] * axis[2];

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			glMultMatrixf((GLfloat*) modelMatrix);
			glTranslatef(cx, cy, cz);
			glRotatef(angle, local_axis[0], local_axis[1], local_axis[2]);
			glTranslatef(-cx, -cy, -cz);
			glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*) modelMatrix);
		} else if (xform_mode == XFORM_SCALE) {
			glLoadIdentity();
			glMultMatrixf((GLfloat*) modelMatrix);
			if (y > press_y)
				glScalef(1.1, 1.1, 1.1);
			else
				glScalef(0.9, 0.9, 0.9);
			glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*) modelMatrix);
			press_y = y;
		}
	}
	glutPostRedisplay();
}

////////////////////////////////////////////////////////////
//    Find the geometry center of the input model 
////////////////////////////////////////////////////////////
void find_center(float& cx, float& cy, float& cz, float& minx, float& maxx,
		float&miny, float &maxy, float &minz, float & maxz) {
	float x, y, z;
	float min_x = 9999, max_x = -9999, min_y = 9999, max_y = -9999;
	float min_z = 9999, max_z = -9999;

	x = y = z = 0;
	for (int i = 0; i < vertexcount; i++) {
		x += vertices[i]->x;
		y += vertices[i]->y;
		z += vertices[i]->z;
		if (min_x > vertices[i]->x)
			min_x = vertices[i]->x;
		if (max_x < vertices[i]->x)
			max_x = vertices[i]->x;
		if (min_y > vertices[i]->y)
			min_y = vertices[i]->y;
		if (max_y < vertices[i]->y)
			max_y = vertices[i]->y;
		if (min_z > vertices[i]->z)
			min_z = vertices[i]->z;
		if (max_z < vertices[i]->z)
			max_z = vertices[i]->z;
	}
	cx = x / (float) vertexcount;
	cy = y / (float) vertexcount;
	cz = z / (float) vertexcount;
	minx = min_x;
	maxx = max_x;
	miny = min_y;
	maxy = max_y;
	minz = min_z;
	maxz = max_z;
}

///////////////////////////////////////////////////////////////

void mykey(unsigned char key, int x, int y) {
	switch (key) {
	case 'q':
		exit(1);
		break;
	case 'f':
		poly_fill = !poly_fill;
		break;
	case 'n':
		if (show_axis == 1)
			show_axis = -1;
		else
			show_axis = 1;
		break;
	case 's':
		use_glsl = !use_glsl;
		if (use_glsl)
			glUseProgramObjectARB(programObject);
		else
			glUseProgramObjectARB(0);
		break;
	case 'g':
		if (transform_global == true)
			transform_global = false;
		else
			transform_global = true;
		break;
	case 't':
		if (transform_global == false) {
			printf("move forwards..\n");
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			glMultMatrixf((GLfloat*) modelMatrix);
			glTranslatef(0.01, 0, 0);
			glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*) modelMatrix);
		}
		break;
	case 'T':
		if (transform_global == false) {
			printf("move backwards..\n");
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			glMultMatrixf((GLfloat*) modelMatrix);
			glTranslatef(-0.01, 0, 0);
			glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*) modelMatrix);
		}
		break;
	}
	glutPostRedisplay();
}

///////////////////////////////////////////////////////////////

void SetupGLSL() {

	GLhandleARB vertexShaderObject;
	GLhandleARB fragmentShaderObject;
	char infobuffer[1000];
	int infobufferlen = 0;

	programObject = glCreateProgramObjectARB();
	vertexShaderObject = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
	fragmentShaderObject = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);

	readShaderSource("test", &vertexShaderSource, &fragmentShaderSource);

	glShaderSourceARB(vertexShaderObject, 1,
			(const GLcharARB**) &vertexShaderSource, NULL);
	glShaderSourceARB(fragmentShaderObject, 1,
			(const GLcharARB**) &fragmentShaderSource, NULL);

	glCompileShaderARB(vertexShaderObject);
	glGetInfoLogARB(vertexShaderObject, 999, &infobufferlen, infobuffer);
	infobuffer[infobufferlen] = 0;
	printf("vertexShaderObject: %s\n", infobuffer);

	glCompileShaderARB(fragmentShaderObject);
	glGetInfoLogARB(fragmentShaderObject, 999, &infobufferlen, infobuffer);
	infobuffer[infobufferlen] = 0;
	printf("fragmentShaderObject: %s\n", infobuffer);

	glAttachObjectARB(programObject, vertexShaderObject);
	glAttachObjectARB(programObject, fragmentShaderObject);

	glLinkProgramARB(programObject);
	glGetInfoLogARB(programObject, 999, &infobufferlen, infobuffer);
	infobuffer[infobufferlen] = 0;
	printf("programObject: %s\n", infobuffer);

	bool result = LoadGLTextures();
	glUseProgramObjectARB(programObject);
	fflush(stdout);

}

bool LoadGLTextures() // Load Bitmaps And Convert To Textures
{
	bool Status = false; // Status Indicator

	IplImage *TextureImage = NULL; // Create Storage Space For The Texture

	if ((TextureImage = cvLoadImage("BG.bmp", CV_LOAD_IMAGE_COLOR))) // Reflection Texture (Spheremap)
	{
		Status = true; // Set The Status To TRUE

		glGenTextures(1, &texture); // Create Three Textures (For Two Images)
		glBindTexture(GL_TEXTURE_2D, texture); // Gen Tex 4 and 5
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TextureImage->width,
				TextureImage->height, 0, GL_RGB, GL_UNSIGNED_BYTE,
				TextureImage->imageData);

		if (TextureImage) // If Texture Exists
		{
			cvReleaseImage(&TextureImage); // Free The Image Structure
		}
	}

	return Status; // Return The Status
}

///////////////////////////////////////////////////////////////

int main(int argc, char** argv) {

	PlyFile* input;

	// get the ply structure and open the file
	FILE * fp;
	if (argc >= 2)
		fp = fopen(argv[1], "r");

	input = read_ply(fp);

	// read in the data
	store_ply(input, &vertices, &faces, &vertexcount, &facecount,
			&vertexnormals, &facenormals);

	// close the file
	close_ply(input);

	find_center(cx, cy, cz, x_min, x_max, y_min, y_max, z_min, z_max);
	printf("geometry center = %f %f %f \n", cx, cy, cz);
	printf("geometry bound = x: %f %f y: %f %f z: %f %f\n", x_min, x_max,
			y_min, y_max, z_min, z_max);
	radius = x_max - x_min;
	if ((y_max - y_min) > radius)
		radius = y_max - y_min;
	if ((z_max - z_min) > radius)
		radius = z_max - z_min;
	radius = radius / 2.0;
	printf(" model radius = %f \n", radius);

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowSize(600, 600);
	win_width = 600;
	win_height = 600;

	glutCreateWindow("Phong Shading");
	glutDisplayFunc(display);
	glutMouseFunc(mymouse);
	glutMotionFunc(mymotion);
	glutKeyboardFunc(mykey);

	glewInit();

	if (glewGetExtension("GL_ARB_fragment_shader") != GL_TRUE
			|| glewGetExtension("GL_ARB_vertex_shader") != GL_TRUE
			|| glewGetExtension("GL_ARB_shader_objects") != GL_TRUE
			|| glewGetExtension("GL_ARB_shading_language_100") != GL_TRUE) {
		fprintf(stderr, "Driver does not support OpenGL Shading Language\n");
		exit(1);
	} else
		fprintf(stderr, "GLSL supported and ready to go\n");

	SetupGLSL();

	glutMainLoop();
}

